package com.txw.javaCore.server.elasticJob;

import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @PROJECT_NAME: JavaCoreB2
 * @DESCRIPTION: @Scheduler + 线程池 + Callable并发多线程处理
 * @Author: 涂玄武
 * @DATE: 2021/3/9 14:08
 */
@Component
@Slf4j
public class SchedulerJobBlogImage {

    //每12个小时执行一次
    private static final String CRON_HOUR="0 0 */12 * * ?";

    private static final String CRON_SECOND="0/10 * * * * ?";

    @Autowired
    private Environment env;

    @Autowired
    private BlogImageService blogImageService;

    //核心线程数
    private static final Integer CorePoolSize = 4;

    //队列中可以容纳/等待被处理的任务数量的阙值的设定
    private static final BlockingQueue BLOCKING_QUEUE = new ArrayBlockingQueue(30);

    //超过了能处理任务的最大线程数的任务的处理策略
    private static final ThreadPoolExecutor.CallerRunsPolicy POLICY = new ThreadPoolExecutor.CallerRunsPolicy();

    //核心线程数;最大线程数;当线程数>核心线程数时那些空闲线程在关闭之前等待新任务到来的最大等待时间;任务队列;拒绝策略
    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(CorePoolSize, 8, 10000, TimeUnit.SECONDS,
            BLOCKING_QUEUE,POLICY);

    //每个线程实现需要实现的功能
    class ImageCallable implements Callable<Boolean>{

        private List<String> list;

        public ImageCallable(List<String> list) {
            this.list = list;
        }

        @Override
        public Boolean call() throws Exception {
            log.info("当前线程执行的任务中列表数据量：{}",list.size());
            if (!CollectionUtils.isEmpty(list)){
                //有废弃的图片待处理
                blogImageService.commonCleanImage(list);
            }
            return true;
        }
    }

    /**
     * 多线程处理
     * 清理条件： is_delete=1 的博客对应的图片 + blog_image 中没有被关联到的图片(一般是点击上传了图片但是最终放弃发微博了)
     * 清理时间： blog_image中create_time为前一天以前的(这样可以防止有用户在执行当前的清理逻辑时刚好上传了图片但是却迟迟不发微博 -> 防止被误删)
     * @Scheduled(cron = CRON_SECOND)
     */
    @Scheduled(cron = CRON_HOUR)
    public void manageImage(){
        try {
            log.info("---清理废弃的微博图片 sheduler定时任务开启---");

            //第一种方式：1W条数据，分而治之，分10组分给10个线程处理，每个线程1K条数据
            //第二种方式：利用分页与线程一一对应，10个线程对应10个分页，每个分页1K条数据

            //开启单线程全部拉取，再分多个线程，分而治之，内存分页去处理
            //模拟数据库查询数据
            List<String> list = new ArrayList<>();
            list.add(env.getProperty("common.file.upload.root.url")+"1.jpg");
            list.add(env.getProperty("common.file.upload.root.url")+"2.jpg");
            list.add(env.getProperty("common.file.upload.root.url")+"3.jpg");
            list.add(env.getProperty("common.file.upload.root.url")+"4.jpg");

            if (!CollectionUtils.isEmpty(list)){
                //采用第一种方式分而治之-开多个线程去处理
                Integer total = list.size();
                Integer pageSize = total/CorePoolSize;

                LinkedList<ImageCallable> jobs = Lists.newLinkedList();
                for (int i=0;i<CorePoolSize;i++){
                    int start = i*pageSize;
                    int end = start+pageSize;

                    //遍历至最后一个线程时
                    if (CorePoolSize-1 == i){
                        start = (CorePoolSize-1)*pageSize;
                        end = total;
                    }

                    jobs.add(new ImageCallable(list.subList(start,end)));
                }

                //发起多线程
                EXECUTOR.invokeAll(jobs);

            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }




}
